package com.study.lock;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class FairLockAndUnfairLockTest {

	private static CountDownLatch countDownLatch;
	
	private static class MyReentrantLock extends ReentrantLock{
		public MyReentrantLock(boolean fair){
			super(fair);
		}
		
		public Collection<Thread> getQueuedThreads(){
			ArrayList<Thread> arrayList = new ArrayList<Thread>(super.getQueuedThreads());
			Collections.reverse(arrayList);
			return arrayList;
		}
	}
	
	private static class Worker extends Thread {
		private Lock lock;
		public Worker(Lock lock){
			this.lock = lock;
		}
		
		public void run(){
			try {
				countDownLatch.await();	
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			for(int i = 0; i <2;i++){
				lock.lock();
				try {
					//TimeUnit.SECONDS.sleep(1);
					System.out.println("lock by [" + getName() + "], waiting by " + ((MyReentrantLock) lock).getQueuedThreads());
				}  finally {
					lock.unlock();
				}
			}
		}
		public String toString(){
			return getName();
		}
	}
	
	public static void main(String[] args) {
		
//		Lock fairLock = new MyReentrantLock(true);
//		testLock(fairLock);
		Lock unfairLock = new MyReentrantLock(false);
		testLock(unfairLock);
	}
	
	private static void testLock(Lock lock){
		countDownLatch = new CountDownLatch(1);
		for(int i =0; i<5;i++){
			Thread thread = new Worker(lock);
			thread.setName("" + i);
			thread.start();
		}
		countDownLatch.countDown();
	}
}
